package com.wbl.train.business.service.transactional;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.wbl.train.business.service.ConfirmOrderService;
import com.wbl.train.business.service.DailyTrainSeatService;
import com.wbl.train.business.service.DailyTrainTicketService;
import com.wbl.train.common.business.dto.ConfirmOrderTicket;
import com.wbl.train.common.business.entity.ConfirmOrderEntity;
import com.wbl.train.common.business.entity.DailyTrainSeatEntity;
import com.wbl.train.common.business.entity.DailyTrainTicketEntity;
import com.wbl.train.common.business.enums.ConfirmOrderStatusEnum;
import com.wbl.train.common.exception.ApplicationException;
import com.wbl.train.common.feign.MemberFeign;
import com.wbl.train.common.member.entity.TicketEntity;
import com.wbl.train.common.result.R;
import com.wbl.train.common.result.ResultCode;
import com.wbl.train.common.utils.SnowUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Author: 王博纶
 * Date: 2024/3/6 9:44
 * FileName: AfterConfirmOrderTransactional
 * Description: 确认订单后的事务处理类
 */
@Slf4j
@Service
public class AfterConfirmOrderTransactional {

    @Resource
    private DailyTrainSeatService dailyTrainSeatService;
    @Resource
    private DailyTrainTicketService dailyTrainTicketService;
    @Resource
    private MemberFeign memberFeign;
    @Lazy // 解决和ConfirmOrderService的循环引用
    @Resource
    private ConfirmOrderService confirmOrderService;

    /**
     *  选中座位后事务的处理
     *      座位表的修改售卖情况 sell 字段
     *      余票详情表修改余票
     *      为会员增加购票记录
     *      更新确认订单表的状态为 "成功"
     *  @param dailyTrainTicket 每日余票
     *  @param finalSeatList 最终选中的座位列表
     *  @param tickets 车票集合
     *  @param confirmOrderEntity 初始的保存确认订单
     */
    @Transactional
    // Seata的全局事务注解
//    @GlobalTransactional
    public void afterDoConfirm(DailyTrainTicketEntity dailyTrainTicket, ArrayList<DailyTrainSeatEntity> finalSeatList, List<ConfirmOrderTicket> tickets, ConfirmOrderEntity confirmOrderEntity ){
//        log.info("seata全局事务ID：{}", RootContext.getXID());
        // 判断finalSeatList的长度，如果未0则证明没有选中座位，购票失败，提示座位不足
        if(CollUtil.isEmpty(finalSeatList)){
            throw new ApplicationException(ResultCode.SEAT_QUANTITY_NOT_ENOUGH,"购票失败，座位不足");
        }
        Date updateTime = new Date();
        ArrayList<TicketEntity> ticketSaveList = new ArrayList<>();
        // 循环finalSeatList：其他字段都是从数据库查出来的、sell字段也单独赋过值了、只需要在单独修改一下更新时间即可
        for (int i = 0; i < finalSeatList.size(); i++) {
            // 修改更新时间
            DailyTrainSeatEntity dailyTrainSeatEntity = finalSeatList.get(i);
            dailyTrainSeatEntity.setUpdateTime(updateTime);
            // ----------------------------------余票详情表修改余票------------------------------------
            updateTicket(dailyTrainTicket, dailyTrainSeatEntity);
            // ----------------------------------构造member端的ticket车票实体类集合------------------------------------
            TicketEntity ticket = structureTicket(dailyTrainTicket, tickets.get(i), updateTime, dailyTrainSeatEntity, confirmOrderEntity);
            ticketSaveList.add(ticket);
        }
        // -------------------------------乘客车票保存--------------------------------------
        R<Boolean> ticketSave = memberFeign.save(ticketSaveList);
        log.info("远程调用member接口，返回：{}",ticketSave);
        if(!ticketSave.getData()){
            throw new ApplicationException(ResultCode.TICKET_ERROR_SAVE,"乘客车票保存失败");
        }
        // -------------------------------每日座位表的售卖情况更新--------------------------------------
        boolean isSuccess = dailyTrainSeatService.updateBatchById(finalSeatList);
        if(!isSuccess){
            throw new ApplicationException(ResultCode.ERROR_DB_BATCH,"座位信息批量更新失败");
        }
        // -------------------------------更新订单保存的状态，设置为完成--------------------------------------
        confirmOrderEntity.setStatus(ConfirmOrderStatusEnum.SUCCESS.getCode())
                .setUpdateTime(new Date());
        LambdaUpdateWrapper<ConfirmOrderEntity> updateWrapper = new LambdaUpdateWrapper<ConfirmOrderEntity>()
                .eq(ConfirmOrderEntity::getId, confirmOrderEntity.getId());
        boolean confirmOrderUpdate = confirmOrderService.update(confirmOrderEntity,updateWrapper);
        if(!confirmOrderUpdate){
            throw new ApplicationException(ResultCode.ORDER_ERROR_UPDATE,"订单更新失败");
        }
    }

    /**
     * 构造ticket实体类
     * @param dailyTrainTicket 每日余票
     * @param ticket 购票信息
     * @param updateTime 当前的更新时间
     * @param dailyTrainSeatEntity 每日座位的实体类
     * @param confirmOrderEntity 确认支付订单的实体类
     * @return ticket实体类
     */
    private static TicketEntity structureTicket(DailyTrainTicketEntity dailyTrainTicket, ConfirmOrderTicket ticket, Date updateTime, DailyTrainSeatEntity dailyTrainSeatEntity,ConfirmOrderEntity confirmOrderEntity) {
        // 调用会员端的feign接口，构造参数，为会员增加一张车票
        return new TicketEntity()
                .setId(SnowUtil.getSnowflakeNextId())
                .setMemberId(confirmOrderEntity.getMemberId())
                .setPassengerId(ticket.getPassengerId())
                .setPassengerName(ticket.getPassengerName())
                .setTrainDate(dailyTrainSeatEntity.getDate())
                .setTrainCode(dailyTrainTicket.getTrainCode())
                .setCarriageIndex(dailyTrainSeatEntity.getCarriageIndex())
                .setSeatRow(dailyTrainSeatEntity.getSeatRow())
                .setSeatCol(dailyTrainSeatEntity.getSeatCol())
                .setStartStation(dailyTrainTicket.getStart())
                .setStartTime(dailyTrainTicket.getStartTime())
                .setEndStation(dailyTrainTicket.getEnd())
                .setEndTime(dailyTrainTicket.getEndTime())
                .setSeatType(dailyTrainSeatEntity.getSeatType())
                .setUpdateTime(updateTime)
                .setCreateTime(updateTime);
    }

    /**
     * 余票详情表修改余票
     * @param dailyTrainTicket 每日余票
     * @param dailyTrainSeatEntity 每日座位
     */
    private void updateTicket(DailyTrainTicketEntity dailyTrainTicket, DailyTrainSeatEntity dailyTrainSeatEntity) {
        LocalDate date = dailyTrainTicket.getDate(); // 获取余票的日期
        String trainCode = dailyTrainTicket.getTrainCode(); // 获取火车编号
        Integer startIndex = dailyTrainTicket.getStartIndex(); // 获取起始站index
        Integer endIndex = dailyTrainTicket.getEndIndex(); // 获取结束站的index
        String sell = dailyTrainSeatEntity.getSell(); // 获取售卖后的售票信息
        char[] chars = sell.toCharArray();
        // 初始化受影响的区间的index
        int minStartIndex = 0;
        Integer maxStartIndex = endIndex - 1;
        Integer minEndIndex = startIndex + 1;
        int maxEndIndex = sell.length();
        for (int i = startIndex - 1; i >= 0 ; i--) {
            if(chars[i] == '1'){
                minStartIndex = i + 1;
                break;
            }
        }
        log.info("影响的出发站的区间：{} ~ {}",minStartIndex,maxStartIndex);
        for (int i = endIndex; i < sell.length(); i++) {
            if(chars[i] == '1'){
                maxEndIndex = i ;
                break;
            }
        }
        log.info("影响的到达站的区间：{} ~ {}",minEndIndex,maxEndIndex);
        // 或者可以通过修改dailyTrainTicket的余票的值来批量操作
        StringBuilder updateSql = getUpdateSql(dailyTrainSeatEntity, date, trainCode, minStartIndex, maxStartIndex, minEndIndex, maxEndIndex);
        LambdaUpdateWrapper<DailyTrainTicketEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                // 设置要更行的字段
                .setSql(updateSql.toString());
        boolean isTicketUpdate = dailyTrainTicketService.update(null, updateWrapper);
        if(!isTicketUpdate){
            throw new ApplicationException(ResultCode.TICKET_ERROR_UPDATE,"余票详情更新失败");
        }
    }

    /**
     * 获取更新每日余票信息的sql
     * @param dailyTrainSeatEntity 每日座位的实体类
     * @param date 当前日期
     * @param trainCode 火车编号
     * @param minStartIndex 最小开始影响期间
     * @param maxStartIndex 最大开始影响期间
     * @param minEndIndex 最小结束影响期间
     * @param maxEndIndex 最大结束期间
     * @return 更新的sql
     */
    private static StringBuilder getUpdateSql(DailyTrainSeatEntity dailyTrainSeatEntity, LocalDate date, String trainCode, int minStartIndex, Integer maxStartIndex, Integer minEndIndex, int maxEndIndex) {
        StringBuilder updateSql = new StringBuilder();
        // 获取当前的座位类型 1，2，3，4
        String seatType = dailyTrainSeatEntity.getSeatType();
        if("1".equals(seatType)){
            // 一等座
            updateSql.append("ydz = ydz - 1 ");
        }else if("2".equals(seatType)){
            // 二等座
            updateSql.append("edz = edz - 1 ");
        }else if("3".equals(seatType)){
            // 软卧
            updateSql.append("rw = rw - 1 ");
        }else if("4".equals(seatType)){
            // 硬卧
            updateSql.append("yw = yw - 1 ");
        }
        updateSql.append("where date = '").append(date).append("'")
                .append(" and train_code = '").append(trainCode).append("'")
                .append(" and start_index >= ").append(minStartIndex)
                .append(" and start_index <= ").append(maxStartIndex)
                .append(" and end_index >= ").append(minEndIndex)
                .append(" and end_index <= ").append(maxEndIndex);
        return updateSql;
    }
}
